package com.example.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.entity.*;
import com.example.entity.enums.UserPreferEnum;
import com.example.mapper.SpuMapper;
import com.example.mapper.UserPreferMapper;
import com.example.service.SkuAttrValueService;
import com.example.service.SkuService;
import com.example.service.SpuAttrValueService;
import com.example.vo.IdHolder;
import com.example.vo.buyer.SpuSkuDetail;
import com.mysql.cj.jdbc.MysqlDataSource;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.common.LongPrimitiveIterator;
import org.apache.mahout.cf.taste.impl.model.jdbc.MySQLJDBCDataModel;
import org.apache.mahout.cf.taste.impl.neighborhood.ThresholdUserNeighborhood;
import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;
import org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity;
import org.apache.mahout.cf.taste.model.JDBCDataModel;
import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;
import org.apache.mahout.cf.taste.recommender.Recommender;
import org.apache.mahout.cf.taste.similarity.UserSimilarity;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Description: 记录和处理用户偏好信息
 * @Author: aimi
 * @Create: 2024-05-15
 **/
@Service
@Slf4j
public class UserPreferService {
    //数据库参数信息
    @Value("${spring.datasource.url}")
    private String dbUrl;
    @Value("${spring.datasource.username}")
    private String dbUser;
    @Value("${spring.datasource.password}")
    private String dbPassword;
    private final String database = "ishop";
    private final String userPreferTable = "user_prefer";


    @Resource
    private UserPreferMapper userPreferMapper;
    @Resource
    private SpuMapper spuMapper;
    @Resource
    private SpuAttrValueService spuAttrValueService;
    @Resource
    private SkuService skuService;
    @Resource
    private SkuAttrValueService skuAttrValueService;

    //获取为用户推荐的商品列表,count为此次推荐的商品数量
    public List<SpuVo> getUserPreferSpuList(int count) {
        List<Long> recommendIds = null;
        Long userId = IdHolder.getUserId();
        boolean exceptionFlag = false;
        //当前是已登录状态，根据偏好进行推荐
        if(userId != null){
            try {
                recommendIds = new ArrayList<>(getUserPreferGoodIds(userId,count));
            } catch (TasteException e) {
                log.info("推荐系统为{}推荐时发生异常：{}",userId,e.getMessage());
                exceptionFlag = true;
            }
        }
        if(userId == null || exceptionFlag){
            List<Long> spuIds = getSpuRandomId(userId);
            if(spuIds.size() >= count){
                recommendIds = new ArrayList<>(spuIds.subList(0,count));
            }else{
                recommendIds = new ArrayList<>(spuIds);
            }
        }


        if (recommendIds.isEmpty()) {
            return Collections.emptyList();
        }

        List<SpuVo> recommendSpuVos = new ArrayList<>();
        for (Long spuId : recommendIds) {
            SpuVo spuVo = getSpuVo(spuId.intValue());
            if (spuVo != null) {
                recommendSpuVos.add(spuVo);
            }
        }
        return recommendSpuVos;
    }

    private List<Long> getUserPreferGoodIds(Long currUserId,int count) throws TasteException {
        ArrayList<Long> recommendIds = new ArrayList<>();
        MysqlDataSource dataSource = new MysqlDataSource();
        dataSource.setUrl(dbUrl);
        dataSource.setUser(dbUser);
        dataSource.setPassword(dbPassword);
        dataSource.setDatabaseName(database);
        JDBCDataModel dataModel = new MySQLJDBCDataModel(dataSource, userPreferTable, "user_id", "goods_id", "score", "stamp");
        // 计算相似度
        UserSimilarity similarity = new PearsonCorrelationSimilarity(dataModel);

        double max = Double.MIN_EXPONENT;
        long maxUid = 0;
        LongPrimitiveIterator userIDs = dataModel.getUserIDs();
        while (userIDs.hasNext()) {
            long userID = userIDs.nextLong();
            // 确保不与自己比较
            if (userID != currUserId) {
                double similarityScore = similarity.userSimilarity(currUserId, userID);
                if (similarityScore > max) {
                    max = similarityScore;
                    maxUid = userID;
                }
            }
        }

        // 定义用户邻居范围
        UserNeighborhood neighborhood = new ThresholdUserNeighborhood(0.1, similarity, dataModel);

        // 创建推荐器
        Recommender recommender = new GenericUserBasedRecommender(dataModel, neighborhood, similarity);

        recommender.recommend(currUserId, count).forEach(recommendation -> {
            recommendIds.add(recommendation.getItemID());
        });
        //当推荐的数量小于count时，在商品列表中寻找补全的id
        if(recommendIds.size() < count){
            List<Long> spuRandomIds = getSpuRandomId(currUserId);
            List<Long> noInPreferIds = spuRandomIds.stream()
                    .filter(id -> !recommendIds.contains(id)).toList();
            if(noInPreferIds.size() >= count - recommendIds.size()){
                recommendIds.addAll(noInPreferIds.subList(0,count - recommendIds.size()));
            }else{
                recommendIds.addAll(noInPreferIds);
            }
        }
        return recommendIds;
    }

    private List<Long> getSpuRandomId(Long userId){
        List<Long> spuIds = userPreferMapper.getSpuRandomId(userId);
        Collections.shuffle(spuIds);
        return spuIds;
    }

    private SpuVo getSpuVo(int spuId) {
        Spu spu = spuMapper.selectById(spuId);
        SpuVo spuVo = new SpuVo();
        BeanUtil.copyProperties(spu, spuVo);
        List<SpuAttrValue> spuAttrValueList = spuAttrValueService.getSpuAttrsBySpuId(spuId);
        spuVo.setBaseAttrs(spuAttrValueList);
        List<SkuVo> skus = skuService.getSkusBySpuId(spuId);
        for (SkuVo skuVo : skus) {
            Long skuId = skuVo.getId();
            skuVo.setSaleAttrs(skuAttrValueService.getSkuAttrsBySkuId(skuId));
        }
        spuVo.setSkus(skus);
        return spuVo;
    }

    public void recordUserScore(Long userId, Long goodsId, UserPreferEnum type) {
        if(userId == null || goodsId == null || type == null){
            return;
        }
        //查询(uid,gid)信息是否存在，如果存在的话，只对点击信息进行累加
        QueryWrapper<UserPrefer> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId).eq("goods_id", goodsId);
        UserPrefer userPrefer = userPreferMapper.selectOne(queryWrapper);
        int score = type.getValue();
        if (userPrefer != null) {
            // 产生重复行为时，对分数进行累加
            userPrefer.setScore(userPrefer.getScore() + score);
            userPrefer.setStamp(System.currentTimeMillis());
            userPreferMapper.update(userPrefer, queryWrapper);
        }
        //插入一条新的用户偏好信息
        else {
            userPrefer = new UserPrefer(userId, goodsId, score, System.currentTimeMillis());
            userPreferMapper.insert(userPrefer);
        }
    }
}